package io.feige.rpc.consumer.network.future;

import io.feige.rpc.consumer.network.listener.InvokeListener;
import io.feige.rpc.exception.ResponseTimeoutException;
import io.feige.rpc.exception.RpcException;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
 
public class InvokeFuture<T> {
  
	private Semaphore semaphore = new Semaphore(0);
	private Throwable cause;   
	private T result;
	private List<InvokeListener<T>> listeners=new ArrayList<InvokeListener<T>>();
	
	public InvokeFuture() {
	}
 
	public void setResult(T result) {
		this.result=result;
		notifyListeners();
		semaphore.release(Integer.MAX_VALUE - semaphore.availablePermits()); 
	}

	public Object getResult(long timeout, TimeUnit unit)  { 
		try {
			if (!semaphore.tryAcquire(timeout, unit)) {
				throw new ResponseTimeoutException();
			}
		} catch (InterruptedException e) {
			throw new RpcException();
		}
		 
		if (this.cause!=null) {
			throw new RpcException(this.cause);
		}
		return result;
	}
  
	public void setCause(Throwable cause) {
		this.cause = cause;
		notifyListeners();
		semaphore.release(Integer.MAX_VALUE - semaphore.availablePermits());  
	}
 
	public Throwable getCause() {
		return cause;
	}
 
	public void addInvokerListener(InvokeListener<T> listener) {
		this.listeners.add(listener);
	}
 
	private void notifyListeners(){
		for (InvokeListener<T> listener : listeners) {
			if (cause!=null) {
				listener.failure(cause);
			}else{
				listener.success(result);
			}
		}
	}
}
